package com.senyint.imsdk;

import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.ConnectionListener;
import org.jivesoftware.smack.PacketInterceptor;
import org.jivesoftware.smack.PacketListener;
import org.jivesoftware.smack.ReconnectionManager;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.filter.PacketTypeFilter;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.ping.packet.Ping;

import com.senyint.imsdk.listener.XMPPConnecionListener;
import com.senyint.imsdk.listener.XMPPIQInterceptor;
import com.senyint.imsdk.listener.XMPPIQListener;
import com.senyint.imsdk.listener.XMPPMessageInterceptor;
import com.senyint.imsdk.listener.XMPPMessageListener;
import com.senyint.imsdk.listener.XMPPPresenceInterceptor;
import com.senyint.imsdk.listener.XMPPPresenceListener;
import com.senyint.imsdk.packet.DeliverySentManager;
import com.senyint.imsdk.packet.PacketReadExtension;
import com.senyint.imsdk.packet.PacketReadIQ;
import com.senyint.imsdk.util.PacketUtil;

public class SmackHelper {
	protected static final String TAG = SmackHelper.class.getSimpleName();
	
    /*****************************/
    private XMPPConnection connection = null ;
    
	private ISdkConfig sdkConfig = null ;
	
	private final static SmackHelper me = new SmackHelper() ;
	
	static{
		try {
			Class.forName("org.jivesoftware.smack.ReconnectionManager");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	private ConnectionListener  mConnectionListener = new XMPPConnecionListener(){
		@Override
		public void onUserConflict() {
			log("connection conflicted.");
			onLogout() ;
		}
		@Override
		public void connectionClosed() {
			log("connection closed.");
			onDisconnect();
		}
		@Override
		public void connectionClosedOnError(Exception ex) {
			super.connectionClosedOnError(ex);
			log("connection error.");
			onDisconnect();
		}
		
		@Override
		public void reconnectionSuccessful() {
			log(TAG, "reconnectionSuccessful");
		}

		@Override
		public void reconnectionFailed(Exception e) {
			log(TAG, "re fail:"+e.getMessage());
		}
	};
	
	private PacketInterceptor mXMPPMessageInterceptor = new XMPPMessageInterceptor() ;
	
	private PacketListener mXMPPMessageListener = new XMPPMessageListener(){
		@Override
		public void onHandleMessage(Message message) {
			super.onHandleMessage(message);
			log("recv",message.toXML());
			onMessageReceved(message);
		}
		@Override
		public void sendPacket(Packet packet) throws SendPacketException {
			onSend(packet);
		}
		
		@Override
		public void onFeedbackServer(final String msgId){
			log("feeed 0000000000////",msgId);
			this.onHandleInBackgroud(new Runnable(){
				@Override
				public void run() {
					try {
						IQ packet = new PacketReadIQ(new PacketReadExtension(msgId));
						packet.setFrom(sdkConfig.getUserJID());
						sendPacket(packet);
					} catch (SendPacketException e) {
						// TODO if 发送失败如何，这里意味着服务器会重复该消息，是否所有发送消息都应该缓存,
						e.printStackTrace();
					}
				}
			});
		}
	};
	
	private PacketListener mXMPPPresenceListener = new XMPPPresenceListener();
	private PacketInterceptor mXMPPPresenceInterceptor = new XMPPPresenceInterceptor();
	private PacketListener mXMPPIQListener = new XMPPIQListener();
	private PacketInterceptor mXMPPIQInterceptor = new XMPPIQInterceptor();
	
	/*****************************/
	 private SmackHelper() {
		 sdkConfig = new ISdkConfig(){
		    private String host = "localhost" ;
		    private String service = "uos" ;
		    private String resource = "smack" ;
		    private String user = null ;
		    private String password = null ;
		    private Integer port = 5222 ;

	    	/**
			 * 
			 */
			private static final long serialVersionUID = 4006508636293955233L;

				@Override
	    		public String getUserName() {
	    			return user;
	    		}

	    		@Override
	    		public String getServiceName() {
	    			return service;
	    		}

	    		@Override
	    		public String getResourceName() {
	    			return resource;
	    		}

	    		@Override
	    		public String getUserJID() {
	    			return user+"@"+getServiceName()+"/"+getResourceName();
	    		}

	    		@Override
	    		public String getPassword() {
	    			return password ;
	    		}

	    		@Override
	    		public void clear() {
	    			user = null ;
	    			password = null ;
	    		}

				@Override
				public String getHost() {
					return host ;
				}

				@Override
				public Integer getPort() {
					return port;
				}

	    	};
	    	
	 }
	 
    public SmackHelper create(ISdkConfig sdkConfig) {
    	this.sdkConfig = sdkConfig;
    	return this ;
    }
    
    public static SmackHelper me(){
    	return me ;
    }
    
    private void log(String tag, String string) {
		System.out.println(tag+"---"+string);
	}

    public boolean isAuthenticated(){
    	 return connection != null && connection.isAuthenticated() ;
    }
    
    /**
     * send to the user
     */
    public void onSend(String to,String msg){
    	if(msg==null||msg.trim().length()==0){
    		return ;
    	}
    	final Message message = new Message();
		if(message.getPacketID() == null){
			message.setPacketID(Packet.nextID());
		}
		message.setBody(msg);
		message.setType(Message.Type.chat);
		message.setFrom(sdkConfig.getUserJID());
		message.setConversation(PacketUtil.createConversationID(sdkConfig.getUserName(), to, null));
		message.setTo(to+"@"+sdkConfig.getServiceName()+"/"+sdkConfig.getResourceName());
		/**
		 * 添加送达服务器的回执
		 */
		DeliverySentManager.addDeliverySentRequest(message);
		
		onSend(message);
    }

	public void setConnectionListener(ConnectionListener mConnectionListener) {
		this.mConnectionListener = mConnectionListener;
	}
	public void setXMPPMessageInterceptor(PacketInterceptor mXMPPMessageInterceptor) {
		this.mXMPPMessageInterceptor = mXMPPMessageInterceptor;
	}
	public void setXMPPMessageListener(PacketListener mXMPPMessageListener) {
		this.mXMPPMessageListener = mXMPPMessageListener;
	}
	public void setXMPPPresenceListener(PacketListener mXMPPPresenceListener) {
		this.mXMPPPresenceListener = mXMPPPresenceListener;
	}
	public void setXMPPPresenceInterceptor(
			PacketInterceptor mXMPPPresenceInterceptor) {
		this.mXMPPPresenceInterceptor = mXMPPPresenceInterceptor;
	}
	public void setXMPPIQListener(PacketListener mXMPPIQListener) {
		this.mXMPPIQListener = mXMPPIQListener;
	}
	public void setXMPPIQInterceptor(PacketInterceptor mXMPPIQInterceptor) {
		this.mXMPPIQInterceptor = mXMPPIQInterceptor;
	}
	
	/**
     * send the packet
     * @param packet
     */
    public void onSend(Packet packet){
    	log("send:"+packet.toXML());
    	if(connection != null && connection.isAuthenticated()){
    		connection.sendPacket(packet);
    	}
    }
    
    public void onConnect(){
    	if (connection!=null&&this.connection.isConnected()) {
			return;
		}
    	if(connection == null){
    		createConnection();
    	}
    	
    	try {
			connection.connect();
		} catch (XMPPException e) {
			e.printStackTrace();
		}
    	
    }
    
    public void onDisconnect(){
    	if (connection != null && this.connection.isConnected()) {
    		connection.disconnect(new Presence(Presence.Type.unavailable));
		}
    }
    
    public void onUserConflict(){
    	if (connection != null && this.connection.isConnected()) {
    		connection.disconnect(new Presence(Presence.Type.unavailable));
		}
    }
    
    public void onLogin(final String uid,final String token,final ILoginListener listener){
    	try {
//                    final XMPPConnection connection = createConnection();
//                    connection.connect();
        		onConnect();
             	log("connected .........."+uid+"/"+token);
                 // Login with an invalid user
                 connection.login(uid , token,sdkConfig.getResourceName());
                 log("login ok,user:"+sdkConfig.getUserName()+","+sdkConfig.getPassword());
                 onLoginSuccess(listener);
         } catch (XMPPException e) {
         	e.printStackTrace();
         	onLoginFailed(listener,e);
         } catch (Exception e) {
         	e.printStackTrace();
         	onLoginFailed(listener,e);
         } 
    }
    
    private void log(String msg) {
    	System.out.println(msg);
	}
    
    public void onLoginSuccess(ILoginListener listener) {
    	if(listener != null){
			listener.onSuccess();
		}
    }
    
    public void onLoginFailed(ILoginListener listener, Exception e) {
		if(listener != null){
			listener.onFailed();
		}
	}

	public void onLogout() {
    	 try {
             if(connection != null){
            	 connection.disconnect(new Presence(Presence.Type.unavailable));
             }
         } catch (Exception e) {
             e.printStackTrace();
         }  
	}
    
    public XMPPConnection createConnection() {
    	if(connection != null)
    		return connection ;
        String host = sdkConfig.getHost();
        Integer port = sdkConfig.getPort();
        // Create the configuration for this new connection
        ConnectionConfiguration config = new ConnectionConfiguration(host, port);
        config.setCompressionEnabled(false);
        config.setSendPresence(true);
        config.setSecurityMode(SecurityMode.disabled);
        config.setSASLAuthenticationEnabled(false);
        config.setReconnectionAllowed(true);
        
        connection = new XMPPConnection(config);
        
        connection.addConnectionListener(mConnectionListener );
		connection.addPacketInterceptor(mXMPPMessageInterceptor ,
				new PacketTypeFilter(Message.class));
		connection.addPacketListener(mXMPPMessageListener,
				new PacketTypeFilter(Message.class));
		connection.addPacketListener(mXMPPPresenceListener,
				new PacketTypeFilter(Presence.class));
		connection.addPacketInterceptor(mXMPPPresenceInterceptor,
				new PacketTypeFilter(Presence.class));
		connection.addPacketListener(mXMPPIQListener,
				new PacketTypeFilter(IQ.class));
		connection.addPacketInterceptor(mXMPPIQInterceptor,
				new PacketTypeFilter(IQ.class));
		
      //for ping reply
  		connection.addPacketListener(new PacketListener() {
  			 @Override  
  			 public void processPacket(Packet p) {
  				 Ping pingIQ = (Ping) p;  
  				 log("RECV:"+pingIQ.toXML());
  			     IQ pongIQ = IQ.createResultIQ(pingIQ);  
  			     connection.sendPacket(pongIQ);
  			     }
  			}, new PacketTypeFilter(Ping.class));
        return connection;
    }
    
    public void onMessageReceved(Message message){
    }
    
    ////////////////////////////////////////////////////
    
    public interface ILoginListener {
    	public void onSuccess();
    	public void onFailed();
    }

	public ISdkConfig getSdkConfig() {
		return sdkConfig;
	}

	public XMPPConnection getConnection() {
		return connection;
	}
    
}
